package org.codefilarete.stalactite.engine.runtime;

import java.util.List;
import java.util.Map;
import javax.sql.DataSource;

import org.codefilarete.reflection.Accessors;
import org.codefilarete.reflection.ReadWritePropertyAccessPoint;
import org.codefilarete.stalactite.engine.runtime.DMLExecutorTest.Toto;
import org.codefilarete.stalactite.mapping.AccessorWrapperIdAccessor;
import org.codefilarete.stalactite.mapping.DefaultEntityMapping;
import org.codefilarete.stalactite.mapping.PersistentFieldHarvester;
import org.codefilarete.stalactite.mapping.id.manager.IdentifierInsertionManager;
import org.codefilarete.stalactite.mapping.id.manager.JDBCGeneratedKeysIdentifierManager;
import org.codefilarete.stalactite.sql.ConnectionConfiguration.ConnectionConfigurationSupport;
import org.codefilarete.stalactite.sql.Dialect;
import org.codefilarete.stalactite.sql.ddl.DDLDeployer;
import org.codefilarete.stalactite.sql.ddl.structure.Column;
import org.codefilarete.stalactite.sql.ddl.structure.Table;
import org.codefilarete.stalactite.sql.test.DatabaseIntegrationTest;
import org.codefilarete.tool.collection.Arrays;
import org.codefilarete.tool.collection.Iterables;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import static org.assertj.core.api.Assertions.assertThat;

/**
 * @author Guillaume Mary
 */
abstract class InsertExecutorAutoGeneratedKeysITTest extends DatabaseIntegrationTest {

	protected DataSource dataSource;
	
	protected Dialect dialect;

	@BeforeEach
	abstract void createDialect();
	
	protected <T extends Table<T>> DefaultEntityMapping<Toto, Integer, T> buildAutoGeneratedKeysPersistenceConfiguration() {
		T targetTable = (T) new Table("Toto");
		PersistentFieldHarvester persistentFieldHarvester = new PersistentFieldHarvester();
		Map<ReadWritePropertyAccessPoint<Toto, ?>, Column<T, ?>> mappedFields = persistentFieldHarvester.mapFields(Toto.class, targetTable);
		ReadWritePropertyAccessPoint<Toto, Integer> primaryKeyAccessor = Accessors.propertyAccessor(persistentFieldHarvester.getField("a"));
		Column<T, Integer> primaryKeyColumn = persistentFieldHarvester.getColumn(primaryKeyAccessor);
		primaryKeyColumn.primaryKey();
		primaryKeyColumn.setAutoGenerated(true);

		// changing mapping strategy to add JDBCGeneratedKeysIdentifierManager and GeneratedKeysReader
		IdentifierInsertionManager<Toto, Integer> identifierGenerator = new JDBCGeneratedKeysIdentifierManager<>(
			new AccessorWrapperIdAccessor<>(primaryKeyAccessor),
			dialect.getGeneratedKeysReaderFactory().build(primaryKeyColumn.getName(), primaryKeyColumn.getJavaType()),
			Integer.class);
		
		DDLDeployer ddlDeployer = new DDLDeployer(dialect.getDdlTableGenerator(), dialect.getDdlSequenceGenerator(), connectionProvider);
		
		ddlDeployer.getDdlGenerator().setTables(Arrays.asSet(targetTable));
		ddlDeployer.deployDDL();
		
		return new DefaultEntityMapping<>(
			Toto.class,
			targetTable,
			mappedFields,
			primaryKeyAccessor,
			identifierGenerator);
	}
	
	@Test
	void insert_generated_pk_real_life() {
		DefaultEntityMapping<Toto, Integer, ?> persistenceConfiguration = buildAutoGeneratedKeysPersistenceConfiguration();
		
		InsertExecutor<Toto, Integer, ?> testInstance = new InsertExecutor<>(
				persistenceConfiguration,
				new ConnectionConfigurationSupport(connectionProvider, 3),
				dialect.getDmlGenerator(),
				dialect.getWriteOperationFactory(),
				3);
		List<Toto> totoList = Arrays.asList(new Toto(17, 23), new Toto(29, 31), new Toto(37, 41), new Toto(43, 53));
		testInstance.insert(totoList);
		
		// Verify that database generated keys were set to Java instances
		assertThat(Iterables.collectToList(totoList, toto -> toto.a)).isEqualTo(Arrays.asList(1, 2, 3, 4));
	}
}
